package edu.kit.aifb.ldstream.cli;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

import org.openrdf.repository.RepositoryException;
import org.openrdf.repository.sail.SailRepository;
import org.openrdf.sail.memory.MemoryStore;
import org.openrdf.sail.nativerdf.NativeStore;

import joptsimple.OptionParser;
import joptsimple.OptionSet;

import edu.kit.aifb.ldstream.env.Environment;
import edu.kit.aifb.ldstream.learner.Learner;
import edu.kit.aifb.ldstream.learner.tasks.mod.ModStatistics;
import edu.kit.aifb.ldstream.repository.RepositoryBuilder;
import edu.kit.aifb.ldstream.stat.util.IDistributionCheckerFactory;
import edu.kit.aifb.ldstream.stat.util.OctaveCheckerFactory;

/**
 * Class for starting  RepositoryBuilder & Learner.
 * 
 * Starting the RepositoryBuilder & Learner after initializing global variables needed for the computation.
 * 
 * @author Holger
 *
 */
public class CLI {

	private static final String V = "v";
	private static final String LOG = "log";
	private static final String L = "l";
	private static final String I = "i";
	private static final String C = "c";
	private static final String R = "r";
	private static final String B = "b";
	private static final String T = "t";
	private static final String IO = "d";

	/**
	 * Used to start RepositoryBuilder & Learner.
	 * 
	 * @param args Starter options, the following are available
	 *        <ul>
	 *        	<li> -c Path of the configuration file </li>
	 *        	<li> -v Verbose mode </li>
	 *        	<li> -l Lookups </li>
	 *        	<li> -i Inferencing </i>
	 *        </ul>
	 *        
	 */
	public static void main(String[] args) {
		OptionParser op = new OptionParser();
		op.accepts(C, "Path of the config file").withRequiredArg().ofType(String.class);
		op.accepts(R, "repository location").withRequiredArg().ofType(String.class);
		op.accepts(L, "Enables lookups");
		op.accepts(LOG, "Enables logging").withRequiredArg().ofType(String.class);
		op.accepts(V, "Verbose mode");
		op.accepts(B, "build repo");
		//needed if the repository gets build step for step (needed for instance occurence)
		op.accepts(I, "Enables inferencing");
		op.accepts(T, "temporal measurements");
		op.accepts(IO, "in-, outdegree");
		OptionSet os = op.parse(args);

		String repoPath;
		if(os.has(IO)){
			Environment.IO_DEGREE = true;
		}
		if(os.has(R)){
			repoPath = (String) os.valueOf(R);
		}else {
			repoPath = Environment.DEFAULT_REPO_LOCATION;
		}
		if(os.has(T)){
			Environment.TIME_MEASUREMENTS = true;
		}
		boolean repoExists = initializeRepository(repoPath);//initOWLIM(repoPath);
		if(repoExists){
			System.out.println("Repo existed");
		} else{
			System.out.println("Repo did not exist");
		}
		ExecutorService threadPool = Executors.newFixedThreadPool(Environment.NUM_THREADS);
		IDistributionCheckerFactory checkerFactory = new OctaveCheckerFactory();
		ModStatistics modStats = new ModStatistics();

		RepositoryBuilder rbuilder = new RepositoryBuilder(threadPool, checkerFactory, modStats);			

		// Reading args and initializing repository
		if(init(args, rbuilder, os, repoExists)){
			rbuilder.start();
			try {
				rbuilder.join();
			} catch (InterruptedException e1) {}

			if(!os.has(B)){
				Learner learner = new Learner(threadPool, checkerFactory, modStats);
				learner.learn();
			}
			shutdown();
		}
		threadPool.shutdown();
		checkerFactory.shutdown();

	}

	private static void shutdown(){
		try {
			Environment.REPO.shutDown();
		} catch (RepositoryException e) {
			e.printStackTrace();
		}
	}

	private static boolean init(String[] args, RepositoryBuilder repoBuilder, OptionSet os, boolean repoExists){
		boolean success = true;

		if(!os.has(V)){
			Environment.VERBOSE = false;
		}
		if(os.has(L)){
			Environment.LOOKUPS = true;
		}
		if(os.has(I)){
			Environment.INFERENCING = true;
		}
		if(os.has(LOG)){
			Environment.LOGGING = true;
			String path = (String) os.valueOf(LOG);
			File logFile = new File(path);
			if(logFile.exists()){
				logFile.delete();
			}
			try {
				logFile.createNewFile();
			} catch (IOException e1) {
				e1.printStackTrace();
			}
			SimpleFormatter formatter = new SimpleFormatter();
			FileHandler fh = null;
			try {
				fh = new FileHandler(path, true);
				fh.setFormatter(formatter);
			} catch (SecurityException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
			Logger log = Logger.getLogger("LDS_Logger");
			log.addHandler(fh);
			log.setLevel(Level.ALL);
			Environment.log = log;
		}
		if(os.has(C)){
			BufferedReader br= null;
			try {
				File f = new File((String) os.valueOf(C));
				br = new BufferedReader(new FileReader(f));
				String stream = br.readLine();
				String[] arg;
				while(stream != null){
					arg = stream.split(" ");
					if(arg.length == 4 && arg[0].equalsIgnoreCase("s")){
						repoBuilder.addStream(arg[1], arg[2], Integer.parseInt(arg[3]));
					}else if(arg.length == 3 && arg[0].equalsIgnoreCase("f")){
						repoBuilder.addFile(arg[1], arg[2], null);
					}else{
						if(!arg[0].isEmpty()){
							System.err.println("Wrong config file format.");
						}
					}
					stream = br.readLine();
				}
			} catch (FileNotFoundException e) {
				success = false;
				e.printStackTrace();
			} catch (IOException e) {
				success = false;
				e.printStackTrace();
			}finally{
				if(br != null){
					try {
						br.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
		}
		return success;
	}


	
	private static boolean initializeRepository(String repoPath){
		boolean exists = true;
		File repoFile = new File(repoPath);
		if(!repoFile.exists()){
			repoFile.mkdir();
			try {
				exists = false;
				repoFile.createNewFile();
			} catch (IOException e) {
				System.err.println("Could not create repository location");
			}
		}
		//	MemoryStore store = new MemoryStore(repoFile);
		//	store.setSyncDelay(150);
		NativeStore store = new NativeStore(repoFile , "spoc,opsc,psoc,cspo" );
		//MySqlStore store = new MySqlStore();

		if(Environment.VERBOSE){
			System.out.println("Started to initialize the repository");
		}
		Environment.REPO = new SailRepository(store); //new ForwardChainingRDFSInferencer(store));
		//	Environment.REPO = new SailRepository(new ForwardChainingRDFSInferencer(store));
		try {
			Environment.REPO.initialize();
		} catch (RepositoryException e) {
			System.err.println("Could not initialize repository");
		}
		if(Environment.VERBOSE){
			System.out.println("Repository initialized at: " + repoFile.getAbsolutePath());
		}

		return exists;
	}

}
